package com.ideal.frame.node.service;

import cn.hutool.json.JSONUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.ideal.frame.institutions.entity.InstitutionsOrganizationTb;
import com.ideal.frame.institutions.entity.QInstitutionsOrganizationTb;
import com.ideal.frame.jpa.DSLPageRequest;
import com.ideal.frame.jpa.JpaRepositoryImpl;
import com.ideal.frame.node.dto.NodeSearchForm;
import com.ideal.frame.node.entity.QRootNodeTb;
import com.ideal.frame.node.entity.RootNodeTb;
import com.ideal.frame.node.form.search.RootNodeSyncParams;
import com.ideal.frame.nodecertificate.entity.NodeCertificate;
import com.ideal.frame.nodecertificate.entity.QNodeCertificate;
import com.querydsl.jpa.impl.JPAQuery;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author zhangdelei
 * @date 2021/8/27
 * @description
 */
@Service
@Slf4j
public class RootNodeService {

    @PersistenceContext
    protected EntityManager entityManager;
    private JpaRepositoryImpl<RootNodeTb, Long> nodeRepo;
    private JpaRepositoryImpl<NodeCertificate, Long> nodeCertificateLongJpaRepository;
    private JpaRepositoryImpl<InstitutionsOrganizationTb, Long> orgRepo;

    QRootNodeTb qNodeTb = QRootNodeTb.rootNodeTb;
    QNodeCertificate qNodeCertificate = QNodeCertificate.nodeCertificate;
    QInstitutionsOrganizationTb qInstitutionsOrganizationTb = QInstitutionsOrganizationTb.institutionsOrganizationTb;
    final ReentrantLock lock = new ReentrantLock();
    @Value("${node.prefix}")
    private String prefix;
    @Value("${node.level}")
    private Integer nodeLevel;
    @Autowired
    private NodeSyncService nodeSyncService;
    @Autowired
    private NodeSyncEventService nodeSyncEventService;

    @PostConstruct
    public void initRepo() {
        nodeCertificateLongJpaRepository = new JpaRepositoryImpl<>(NodeCertificate.class, entityManager);
        nodeRepo = new JpaRepositoryImpl<>(RootNodeTb.class, entityManager);
        orgRepo = new JpaRepositoryImpl<>(InstitutionsOrganizationTb.class, entityManager);
    }

    /**
     * 获取所有未生成证书的节点
     *
     * @return
     */
    public List<RootNodeTb> getAllNotHasCerNodes() {
        return this.nodeRepo.selectFrom(qNodeTb)
                .where(nodeCertificateLongJpaRepository.selectFrom(qNodeCertificate).where(qNodeCertificate.nodeType.eq(0)).where(qNodeCertificate.nodeId.eq(qNodeTb.id)).notExists()).fetch();
    }

    /**
     * 根据同步时间获取根节点
     *
     * @param params
     * @return
     */
    public List<RootNodeTb> findBySyncTime(RootNodeSyncParams params) {
        JPAQuery<RootNodeTb> query = nodeRepo.selectFrom(qNodeTb).orderBy(qNodeTb.updatedate.asc());
        if (!StringUtils.isEmpty(params.getSyncTime())) {
            query.where(qNodeTb.updatedate.gt(params.getSyncTime()));
        }
        return query.fetch();
    }

    /**
     * 同步根节点数据
     *
     * @param list
     */
    @Transactional(rollbackFor = Exception.class)
    public void syncRootData(List<RootNodeTb> list) {
        Assert.isTrue(nodeLevel == -1, "操作权限不足，只有根节点才可操作");
        if (list != null && list.size() > 0) {
            log.info("根节点同步================================================" + list.size());
            for (int i = 0; i < list.size(); i++) {
                log.info("执行for循环================================================" + i);
                RootNodeTb t = list.get(i);
                log.info("根节点同步数据开始================================================1");
                long id = t.getId();
                RootNodeTb root = getDataBySyncId(id);
                log.info("根节点同步数据开始================================================2");
                t.setSyncId(id);
                if (root != null) {
                    t.setId(root.getId());
                } else {
                    t.setId(null);
                }
                log.info("根节点同步数据================================================" + JSONUtil.toJsonStr(t));
                nodeRepo.saveAndFlush(t);
            }
            log.info("新增同步时间节点================================================" + prefix);
            nodeSyncEventService.saveSyncEvent(0, list.get(list.size() - 1).getUpdatedate(), prefix);
        }
    }

    /**
     * 获取所有需要同步的根节点列表(自己不需要进行同步)
     *
     * @return
     */
    public List<RootNodeTb> allData() {
        return nodeRepo.selectFrom(qNodeTb).where(qNodeTb.prefixCode.notIn(prefix)).fetch();
    }

    /**
     * 分页查询
     *
     * @param nodeSearchForm
     * @return
     */
    public Page<RootNodeTb> getPage(NodeSearchForm nodeSearchForm) {
        DSLPageRequest pageRequest = nodeSearchForm.asDslPageRequest();
        if (nodeSearchForm.getNodeId() != null) {
            pageRequest.addWhere(qNodeTb.id.eq(nodeSearchForm.getNodeId()));
        }
        if (nodeSearchForm.getNodeName() != null) {
            pageRequest.addWhere(qNodeTb.nodename.like("%" + nodeSearchForm.getNodeName() + "%"));
        }
        if (nodeSearchForm.getIp() != null) {
            pageRequest.addWhere(qNodeTb.nodeip.like("%" + nodeSearchForm.getIp() + "%"));
        }
        if (nodeSearchForm.getPort() != null) {
            pageRequest.addWhere(qNodeTb.nodeport.like("%" + nodeSearchForm.getPort() + "%"));
        }
        pageRequest.addWhere(qNodeTb.isdelete.isFalse());
        return nodeRepo.findAll(pageRequest);
    }

    public RootNodeTb getById(Long id) {
        Optional<RootNodeTb> nodeTb = nodeRepo.findById(id);
        return nodeTb.isPresent() ? nodeTb.get() : null;
    }

    @Transactional(rollbackFor = Exception.class)
    public void saveNode(RootNodeTb nodeTb) {
        Assert.isTrue(nodeLevel == 0, "操作权限不足，只有原始根节点才可操作");
        lock.lock();
        try {
            Optional<InstitutionsOrganizationTb> optional = orgRepo
                    .findOne(qInstitutionsOrganizationTb.id.eq(nodeTb.getInstitutionsOrganizationTb().getId())
                            .and(qInstitutionsOrganizationTb.delFlag.isFalse()));
            Assert.isTrue(optional.isPresent(), "机构不存在");
            InstitutionsOrganizationTb organizationTb = optional.get();
//            Assert.isTrue(!(StrUtil.isNotEmpty(organizationTb.getPrefix())
//                    || ObjectUtil.isNotEmpty(organizationTb.getRootNodeId())), "机构已被绑定");
            Date date = new Date();
            if (nodeTb.getId() == null) {
                nodeTb.setInsertdate(date);
                nodeTb.setPrefixCode(prefix + getCount());
                Assert.isTrue(!(StrUtil.isNotEmpty(organizationTb.getPrefix())
                        || ObjectUtil.isNotEmpty(organizationTb.getRootNodeId())), "机构已被绑定");
            } else {
                RootNodeTb rootNodeTb = nodeRepo.findById(nodeTb.getId()).get();
                if (ObjectUtil.notEqual(nodeTb.getInstitutionsOrganizationTb().getId(), rootNodeTb.getInstitutionsOrganizationTb().getId())) {
                    Assert.isTrue(!(StrUtil.isNotEmpty(organizationTb.getPrefix())
                            || ObjectUtil.isNotEmpty(organizationTb.getRootNodeId())), "机构已被绑定");
                    long execute = orgRepo.updateClause().setNull(qInstitutionsOrganizationTb.rootNodeId)
                            .where(qInstitutionsOrganizationTb.id.eq(rootNodeTb.getInstitutionsOrganizationTb().getId())).execute();
                    log.info("更新的行============>>>:{}", execute);
                }
            }
            nodeTb.setUpdatedate(date);
            nodeTb.setIsdelete(false);
            nodeTb.setStatus(Boolean.TRUE);
            nodeTb.setNettymonitor(false);
            nodeRepo.saveAndFlush(nodeTb);
            orgRepo.updateClause().set(qInstitutionsOrganizationTb.rootNodeId, nodeTb.getId())
                    .where(qInstitutionsOrganizationTb.id.eq(nodeTb.getInstitutionsOrganizationTb().getId())).execute();
            // 同步根节点到其他节点
            nodeSyncService.syncRootData();
        } finally {
            lock.unlock();
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void deleteNode(Long nodeId) {
        Assert.isTrue(nodeLevel == 0, "操作权限不足，只有原始根节点才可操作");
        nodeRepo.updateClause().set(qNodeTb.isdelete, true).set(qNodeTb.updatedate, new Date()).where(qNodeTb.id.eq(nodeId)).execute();
        // 同步根节点到其他节点
        nodeSyncService.syncRootData();
    }

    /**
     * 获取根节点数量
     *
     * @return
     */
    private long getCount() {
        return nodeRepo.selectFrom(qNodeTb).where(qNodeTb.isdelete.isFalse()).fetchCount() + 1;
    }

    /**
     * 根据前缀获取根节点数据
     *
     * @param syncId
     * @return
     */
    private RootNodeTb getDataBySyncId(long syncId) {
        return nodeRepo.selectFrom(qNodeTb).where(qNodeTb.syncId.eq(syncId)).fetchFirst();
    }

    public List<RootNodeTb> getAllNodes() {
        // 获取当前节点
        RootNodeTb tb = new RootNodeTb();
        tb.setIsdelete(false);
        List<RootNodeTb> list = nodeRepo.findAll(Example.of(tb));
        return list;
    }

    /**
     * 获取所有取消心跳检测的节点
     *
     * @return
     */
    public List<RootNodeTb> getAllCancelHeartbeatNodes() {
        RootNodeTb nodeTb = new RootNodeTb();
        nodeTb.setIsdelete(false);
        nodeTb.setNettymonitor(false);
        List<RootNodeTb> nodeList = nodeRepo.findAll(Example.of(nodeTb));
        return nodeList;
    }

    @Transactional(rollbackFor = Exception.class)
    public void save(RootNodeTb nodeTb) {
        if (nodeTb.getId() != null) {
            // 更新
            nodeTb.setUpdatedate(new Date());
        } else {
            // 新增
            nodeTb.setInsertdate(new Date());
            nodeTb.setUpdatedate(new Date());
            nodeTb.setIsdelete(false);
        }
        nodeRepo.save(nodeTb);
    }
}
